package edu.ucla.whi.wanda;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.restlet.ext.wadl.ParameterInfo;
import org.restlet.ext.wadl.ParameterStyle;
import org.restlet.resource.Get;

import com.amazonaws.services.simpledb.model.Item;
import com.amazonaws.services.simpledb.model.SelectRequest;
import com.amazonaws.services.simpledb.model.SelectResult;
import com.amazonaws.util.json.JSONArray;
import com.amazonaws.util.json.JSONException;
import com.amazonaws.util.json.JSONObject;

public class MeasurementSearchResource extends BaseResource {
	public static final String path = "/measurements/search";
	public static final String description = "Search Measreuments";
	public static final String outputParamName = "MeasurementList";
	
	
	public MeasurementSearchResource() {
		super();
		
		initDescribe(path, description, outputParamName);
	}
	
	@Override
	protected List<ParameterInfo> getParams() {
		List<ParameterInfo> params = new LinkedList<ParameterInfo>();
		
		// optional
		params.add(new ParameterInfo("where", false, "xsd:string", ParameterStyle.QUERY, 
				"WHERE condition for Measurement"));
		params.add(new ParameterInfo("whereSubject", false, "xsd:string", ParameterStyle.QUERY, 
				"WHERE condition for Subject"));
		params.add(new ParameterInfo("whereSensor", false, "xsd:string", ParameterStyle.QUERY, 
				"WHERE condition for Sensor"));
		
		return params;
	}
	
	private Set<String> getIds(String domain, String where) {
		Set<String> ids = new HashSet<String>();

		// get all items
		String next = null;
		do {
			SelectRequest request = buildSelectRequest(domain, where, null, next, true);
			SelectResult result = getDB().select(request);
	        for (Item item : result.getItems()) {
	        	ids.add(item.getName());
	        }
	        
	        next = result.getNextToken();
		} while (next != null);

		return ids;
	}
	
	private JSONArray getMeasurements(Set<String> subjectIds, Set<String> sensorIds) {
        JSONArray array = new JSONArray();

        // prevent dumping of the entire DB!
        if (subjectIds.size() + sensorIds.size() == 0) {
        	return array;
        }
		
		String all = "*";
		
		// append "*" to empty Ids
		if (subjectIds.size() == 0) {
			subjectIds.add(all);
		}
		if (sensorIds.size() == 0) {
			sensorIds.add(all);
		}
		
        for (String subjectId : subjectIds) {
        	for (String sensorId : sensorIds) {
        		List<String> parts = new LinkedList<String>();
        		
        		if (params.containsKey("where")) {
        			parts.add(params.get("where"));
        		}

        		if (!subjectId.equals(all)) {
        			parts.add("SubjectId = '" + subjectId + "'");
        		}
        		
        		if (!sensorId.equals(all)) {
        			parts.add("SensorId = '" + sensorId + "'");
        		}
            		
        		String where = parts.size() > 0 ? StringUtils.join(" AND ", parts) : null;
        		
        		String next = null;
        		do {
        			SelectRequest request = buildSelectRequest(MEASUREMENTS_DOMAIN, where, null, next, false);
        			SelectResult result = getDB().select(request);
        	        for (Item item : result.getItems()) {
        	        	array.put(makeJson(item.getName(), item.getAttributes()));
        	        }
        		} while (next != null);
        	}
        }
        
        return array;
	}
	
	@Get("json")
	public String search() {
		// get all sensors
		Set<String> sensorIds = new HashSet<String>();
		if (params.containsKey("whereSensor")) {
			sensorIds = getIds(SENSORS_DOMAIN, params.get("whereSensor"));
		}
		
		// get all subjects
		Set<String> subjectIds = new HashSet<String>();
		if (params.containsKey("whereSubject")) {
			subjectIds = getIds(SUBJECTS_DOMAIN, params.get("whereSubject"));
		}

        // Constructing JSON
        JSONObject json = new JSONObject();
        try {
	        json.put("measurements", getMeasurements(subjectIds, sensorIds));
        }
        catch (JSONException e) {
        	throw new RuntimeException(e.toString());
        }
        
        return json.toString();
	}
}